/******************************************************
* Team WB
* Brett Cate, William Kim
* 
* Debug Monitor ADT Source
*******************************************************/

#include "debugMonitor.h"

/*
ADT - DebugMonitor
	Data Type Description:
	Contains debug state and breakpoint addresses. Note you need not implement the latter in this phase
		int     debugState;		// used to inform the CPU
		ushort	breakPoints[MAXBREAKPOINTS];
		ushort	currentPCValue;
		ushort	memDumpStart;

	Operations:
		Initialize		// new a debug monitor and initialize the above variables
		LoadProgram		// open a text file containing a loadable program and read that text into
						// main memory starting at the load address given in the first line of the
						// file (use the same format as the LC-3 load file)
		RunProgram		// sets cpu->PC to 0x3000, start of user program; sets debugState to RUN
		StepProgram		// sets cpu->PC to 0x3000, sets debugState to STEP
		ShowRegisters	// refreshes register contents; returns to cpu_run()
		ShowMemory		// gets start address to dump and shows 16 words of memory in following format:
						// address	byte  byte
						// x3400    2A    01
						//           ^     ^
						//           |     +-- byte at x3401
						//           +-------- byte at x3400
		ShowAll			// refreshes all screen variables

		appropriate getters and setters
*/

/*
Constructs a new debug monitor and initializes its fields.

parameter (the_cpu): The CPU to debug.
parameter (the_error): Pointer to the int value of the error code.

return value : A pointer to the new debug monitor object.
*/
DebugMonitorPtr debugMonitor_new(CPUPtr the_cpu, int* the_error) {
	DebugMonitorPtr dmPtr;
	int i;
	if (the_cpu == NULL) {
		*the_error = NULL_POINTER_ERROR;
		return NULL;
	}
	dmPtr = (DebugMonitorPtr)malloc(sizeof(DebugMonitor));
	if (dmPtr == NULL) {
		*the_error = OUT_OF_MEM_ERROR;
		return NULL;
	}
	dmPtr->breakPoints = (ushort*)malloc(sizeof(ushort)*MAXBREAKPOINTS);
	for (i = 0; i < MAXBREAKPOINTS; i++)
		dmPtr->breakPoints[i] = 0;
	dmPtr->breakpoint_index = 0;
	dmPtr->cpu_ptr = the_cpu;
	dmPtr->currentPCValue = MEM_START;
	dmPtr->debugState = STOP;
	dmPtr->memDumpStart = MEM_START;
	*the_error = NO_ERROR;
	return dmPtr;
}

/*
Opens a text file containing a loadable program and reads that text into
main memory starting at the load address given in the first line of the file

parameter (the_debug_monitor_ptr): the debug monitor object.
parameter (the_file_name): The name of the file to load.

return value: Error code.
*/
int debugMonitor_loadProgram(DebugMonitorPtr the_debug_monitor_ptr, char* the_file_name) {
	int word, address;
	FILE* program;
	
	if (the_debug_monitor_ptr == NULL) 
		return NULL_POINTER_ERROR;

	program = fopen(the_file_name, "r");
	fscanf(program, "%X\n", &address); // starting address
	fscanf(program, "%X\n", &word);
	while (word != 0) {
		memory_storeWordTo(the_debug_monitor_ptr->cpu_ptr->mainMemory,(ushort)address,(ushort)word);
		address += BYTES_PER_WORD;
		fscanf(program, "%X", &word);
	}
	return NO_ERROR;
}

/*
Sets cpu->PC to 0x3000, start of user program; sets debugState to RUN.

parameter (the_debug_monitor_ptr): the debug monitor object.

return value: Error code.
*/
int debugMonitor_runProgram(DebugMonitorPtr the_debug_monitor_ptr) {
	if (the_debug_monitor_ptr == NULL) 
		return NULL_POINTER_ERROR;

	cpu_setPC((CPUPtr)the_debug_monitor_ptr->cpu_ptr, the_debug_monitor_ptr->currentPCValue);
	the_debug_monitor_ptr->debugState = RUN;
	return NO_ERROR;
}

/*
Sets cpu->PC to 0x3000, sets debugState to STEP.

parameter (the_debug_monitor_ptr): the debug monitor object.

return value: Error code.
*/
int debugMonitor_stepProgram(DebugMonitorPtr the_debug_monitor_ptr) {
	if (the_debug_monitor_ptr == NULL) 
		return NULL_POINTER_ERROR;

	cpu_setPC(the_debug_monitor_ptr->cpu_ptr, the_debug_monitor_ptr->currentPCValue);
	the_debug_monitor_ptr->debugState = STEP;
	return NO_ERROR;
}

/*
Prompts for and adds a breakpoint to this debug monitor.
*/
void debugMonitor_addBreakpoint(DebugMonitorPtr the_debug_monitor_ptr) {
	int breakpoint_address;
	printw("\nEnter a memory address to set a breakpoint at: \n");
	scanw("%4X",&breakpoint_address);
	printw("\n");
	the_debug_monitor_ptr->breakPoints[the_debug_monitor_ptr->breakpoint_index] = breakpoint_address;
	(the_debug_monitor_ptr->breakpoint_index)++;
	erase();
}

/*
Refreshes register contents; returns to cpu_run().

parameter (the_debug_monitor_ptr): the debug monitor object.

return value: Window for the registers.
*/
WINDOW* debugMonitor_showRegisters(DebugMonitorPtr the_debug_monitor_ptr) {
	WINDOW* registerFile_window;
	int i;
	registerFile_window = newwin(21,16,1,1);
	box(registerFile_window,0,0);
	mvwaddstr(registerFile_window,1,1,"Register File");
	for (i=0; i<=0xF; i++) {
		mvwprintw(registerFile_window,i+3,1,"$R%X : %04X",i,*the_debug_monitor_ptr->cpu_ptr->registerFile[i]);
	}
	wrefresh(registerFile_window);
	return registerFile_window;
}

/*
Gets start address to dump and shows 16 words of memory in following format:
						address	byte  byte
						x3400    2A    01
						          ^     ^
						          |     +-- byte at x3401
						          +-------- byte at x3400

parameter (the_debug_monitor_ptr): the debug monitor object.

return value: Window for the memory.
*/
WINDOW* debugMonitor_showMemory(DebugMonitorPtr the_debug_monitor_ptr) {
	WINDOW* memory_window;
	int i;
	memory_window = newwin(21,16,1,41);
	box(memory_window,0,0);
	mvwaddstr(memory_window,1,5,      "Memory");
	mvwaddstr(memory_window,3,1,      "Address  Hi Lo");
	for (i = 0; i < 16; i++) {
		mvwprintw(memory_window,i+4,1,"x%04X  : %02X %02X",MEM_START+i*2,the_debug_monitor_ptr->cpu_ptr->mainMemory[i*2],the_debug_monitor_ptr->cpu_ptr->mainMemory[i*2+1]);
	}
	wrefresh(memory_window);
	return memory_window;
}

/*
Refreshes all screen variables.

parameter (the_debug_monitor_ptr): the debug monitor object.

return value: Window for the whole cpu.
*/
WINDOW* debugMonitor_showAll(DebugMonitorPtr the_debug_monitor_ptr) {
	WINDOW* master_window;
	WINDOW* alu_window;
	WINDOW* console_window;
	master_window = newwin(23,80,0,0);
	box(master_window,0,0);

	mvwprintw(master_window,12,18,"IR  : %04X  PC  : %04X",*the_debug_monitor_ptr->cpu_ptr->IR,*the_debug_monitor_ptr->cpu_ptr->PC);
	mvwprintw(master_window,13,18,"SW  : %04X",*the_debug_monitor_ptr->cpu_ptr->SW);
	mvwprintw(master_window,14,18,"MDR : %04X  MAR : %04X",*the_debug_monitor_ptr->cpu_ptr->MDR,*the_debug_monitor_ptr->cpu_ptr->MAR);
	


	wrefresh(master_window);



	alu_window = newwin(8,22,1,18);
	box(alu_window,0,0);
	mvwaddstr(alu_window,1,4,"ALU Registers");
	mvwprintw(alu_window,3,1,"A : %04X  B  : %04X",*the_debug_monitor_ptr->cpu_ptr->alu->reg_A,*the_debug_monitor_ptr->cpu_ptr->alu->reg_B);
	mvwprintw(alu_window,4,1,"R : %04X  R2 : %04X",*the_debug_monitor_ptr->cpu_ptr->alu->reg_R,*the_debug_monitor_ptr->cpu_ptr->alu->reg_R2);
	wrefresh(alu_window);

	debugMonitor_showRegisters(the_debug_monitor_ptr);
	debugMonitor_showMemory(the_debug_monitor_ptr);

/*	console_window = newwin(20,80,25,0);
	box(console_window,0,0);
	wrefresh(console_window); */

	mvprintw(24,2,"Press any key to continue...\n");
	refresh();

	return master_window;
}

/*
parameter (the_debug_monitor_ptr): the debug monitor object.
parameter (the_error) : the error code.

return value: the debug state of this debug monitor.
*/
int debugMonitor_getDebugState(DebugMonitorPtr the_debug_monitor_ptr, int* the_error) {
	if (the_debug_monitor_ptr == NULL) {
		*the_error = NULL_POINTER_ERROR;
		return 0;
	}

	return the_debug_monitor_ptr->debugState;
}

/*
Sets the debug state.

parameter (the_debug_monitor_ptr): the debug monitor object.

return value: Error code.
*/
int debugMonitor_setDebugState(DebugMonitorPtr the_debug_monitor_ptr, int the_debug_state) {
	if (the_debug_monitor_ptr == NULL) 
		return NULL_POINTER_ERROR;
	if (the_debug_state < STOP || the_debug_state > BREAK)
		return ILLEGAL_ARG;

	the_debug_monitor_ptr->debugState = the_debug_state;
	return NO_ERROR;
}

/*
parameter (the_debug_monitor_ptr): the debug monitor object.
parameter (the_error) : the error code.

return value: the array of break points of this debug monitor.
*/
ushort* debugMonitor_getBreakPoints(DebugMonitorPtr the_debug_monitor_ptr, int* the_error) {
	if (the_debug_monitor_ptr == NULL) {
		*the_error = NULL_POINTER_ERROR;
		return 0;
	}

	*the_error = NO_ERROR;
	return the_debug_monitor_ptr->breakPoints;
}

/*
Sets the break points. 

parameter (the_debug_monitor_ptr): the debug monitor object.

return value: Error code.
*/
int debugMonitor_setBreakPoints(DebugMonitorPtr the_debug_monitor_ptr, ushort* the_break_points) {
	if (the_debug_monitor_ptr == NULL || the_break_points == NULL) 
		return NULL_POINTER_ERROR;

	the_debug_monitor_ptr->breakPoints = the_break_points;
	return NO_ERROR;
}

/*
parameter (the_debug_monitor_ptr): the debug monitor object.
parameter (the_error) : the error code.

return value: the current PC value.
*/
ushort debugMonitor_getCurrentPCValue(DebugMonitorPtr the_debug_monitor_ptr, int* the_error) {
	if (the_debug_monitor_ptr == NULL) {
		*the_error = NULL_POINTER_ERROR;
		return 0;
	}

	*the_error = NO_ERROR;
	return the_debug_monitor_ptr->currentPCValue;
}

/*
Sets the current PC value.

parameter (the_debug_monitor_ptr): the debug monitor object.

return value: Error code.
*/
int debugMonitor_setCurrentPCValue(DebugMonitorPtr the_debug_monitor_ptr, ushort the_PC_value) {
	if (the_debug_monitor_ptr == NULL) 
		return NULL_POINTER_ERROR;

	the_debug_monitor_ptr->currentPCValue = the_PC_value;
	return NO_ERROR;
}

/*
parameter (the_debug_monitor_ptr): the debug monitor object.
parameter (the_error) : the error code.

return value: the the memory dump start location.
*/
ushort debugMonitor_getMemDumpStart(DebugMonitorPtr the_debug_monitor_ptr, int* the_error) {
	if (the_debug_monitor_ptr == NULL) {
		*the_error = NULL_POINTER_ERROR;
		return 0;
	}

	*the_error = NO_ERROR;
	return the_debug_monitor_ptr->memDumpStart;
}

/*
Sets the memory dump start location.

parameter (the_debug_monitor_ptr): the debug monitor object.

return value: Error code.
*/
int debugMonitor_setMemDumpStart(DebugMonitorPtr the_debug_monitor_ptr, ushort the_mem_dump_start) {
	if (the_debug_monitor_ptr == NULL) 
		return NULL_POINTER_ERROR;

	the_debug_monitor_ptr->memDumpStart = the_mem_dump_start;
	return NO_ERROR;
}
